1
2
3
4
5 package com.jguild.jrpm.ui;
6
7 import java.awt.BorderLayout;
8 import java.awt.Component;
9 import java.awt.Container;
10 import java.awt.Dimension;
11 import java.awt.GridBagConstraints;
12 import java.awt.GridBagLayout;
13 import java.awt.event.ActionEvent;
14 import java.awt.event.ActionListener;
15 import java.awt.event.KeyEvent;
16 import java.awt.event.WindowAdapter;
17 import java.awt.event.WindowEvent;
18 import java.io.File;
19 import java.io.FileFilter;
20 import java.io.IOException;
21 import java.util.ArrayList;
22 import java.util.Comparator;
23 import java.util.Enumeration;
24 import java.util.Hashtable;
25 import java.util.List;
26 import java.util.Locale;
27 import java.util.Set;
28 import java.util.StringTokenizer;
29 import java.util.TreeSet;
30 import java.util.Vector;
31
32 import javax.swing.BoxLayout;
33 import javax.swing.DefaultComboBoxModel;
34 import javax.swing.JComboBox;
35 import javax.swing.JDialog;
36 import javax.swing.JFileChooser;
37 import javax.swing.JFrame;
38 import javax.swing.JLabel;
39 import javax.swing.JMenu;
40 import javax.swing.JMenuBar;
41 import javax.swing.JMenuItem;
42 import javax.swing.JOptionPane;
43 import javax.swing.JPanel;
44 import javax.swing.JProgressBar;
45 import javax.swing.JScrollPane;
46 import javax.swing.JSplitPane;
47 import javax.swing.JTextArea;
48 import javax.swing.JTree;
49 import javax.swing.ToolTipManager;
50 import javax.swing.event.TreeModelEvent;
51 import javax.swing.event.TreeModelListener;
52 import javax.swing.event.TreeSelectionEvent;
53 import javax.swing.event.TreeSelectionListener;
54 import javax.swing.tree.DefaultTreeCellRenderer;
55 import javax.swing.tree.TreeModel;
56 import javax.swing.tree.TreePath;
57
58 import org.apache.log4j.Logger;
59 import org.apache.log4j.varia.NullAppender;
60
61 import com.jguild.jrpm.io.RPMFile;
62 import com.jguild.jrpm.io.datatype.DataTypeIf;
63
64 /***
65 * A browser to search for rpm files in a given directory. The rpms are schown
66 * in a tree. The tree display the in rpm defined groups. So every rpm that
67 * point to the same group are sorted into the same node in the tree. This
68 * browser should demonstrate the functionality of jRPM.
69 *
70 * @author kuss
71 */
72 public class RPMBrowser extends JFrame implements ActionListener {
73
74 private InfoPanel infoPanel = new InfoPanel(new String[] { "name",
75 "version", "release", "summary", "description", "vendor", "url",
76 "copyright", "distribution", "disturl", "filenames"});
77
78 private RPMGroupTreeModel treeModel = new RPMGroupTreeModel();
79
80 /***
81 * Creates a new RPMBrowser object.
82 */
83 public RPMBrowser() {
84 setTitle("RPM Browser");
85 setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
86 setSize(new Dimension(600, 400));
87 setJMenuBar(createMenuBar());
88
89 Container content = getContentPane();
90 JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
91 new JScrollPane(createGroupTree()), new JScrollPane(
92 createRPMInfoPane()));
93 splitPane.setDividerLocation(200);
94 content.add(splitPane);
95
96
97 setLocationRelativeTo(null);
98
99 addWindowListener(new WindowAdapter() {
100
101 public void windowClosing(WindowEvent e) {
102 quit();
103 }
104 });
105 }
106
107
108
109
110 public void actionPerformed(ActionEvent e) {
111 if (e.getActionCommand().equals("Exit")) {
112 quit();
113 } else if (e.getActionCommand().equals("ChooseDir")) {
114 chooseBaseDir();
115 }
116 }
117
118 /***
119 * Main method to start the RPMBrowser
120 *
121 * @param args
122 * The start arguments
123 */
124 public static void main(String[] args) {
125 Locale.setDefault(Locale.ENGLISH);
126
127 if (!Logger.getLogger("com.jguild.jrpm").getAllAppenders()
128 .hasMoreElements()) {
129 Logger.getLogger("com.jguild.jrpm").addAppender(new NullAppender());
130 }
131
132 RPMBrowser frame = new RPMBrowser();
133 frame.setVisible(true);
134 }
135
136 private List getRPMFiles(File dir, MyMonitor monitor) {
137 List rpms = new ArrayList();
138 File[] list = dir.listFiles(new FileFilter() {
139
140 public boolean accept(File f) {
141 return f.isDirectory()
142 || f.getName().toLowerCase().endsWith(".rpm");
143 }
144 });
145
146 monitor.setMaximum(monitor.getMaximum() + list.length);
147
148 for (int pos = 0; pos < list.length; pos++) {
149 if (list[pos].isDirectory()) {
150 rpms.addAll(getRPMFiles(list[pos], monitor));
151 } else {
152 rpms.add(list[pos]);
153 }
154
155 monitor.incProgress();
156 }
157
158 Thread.yield();
159
160 return rpms;
161 }
162
163 private void chooseBaseDir() {
164 JFileChooser dirChooser = new JFileChooser();
165 dirChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
166
167 if (dirChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
168 createRPMTree(dirChooser.getSelectedFile());
169 }
170 }
171
172 private JTree createGroupTree() {
173 JTree tree = new JTree(treeModel);
174 ToolTipManager.sharedInstance().registerComponent(tree);
175 tree.setCellRenderer(new MyRenderer());
176 tree.addTreeSelectionListener(new TreeSelectionListener() {
177
178 public void valueChanged(TreeSelectionEvent e) {
179 JTree tree = (JTree) e.getSource();
180 Object node = tree.getLastSelectedPathComponent();
181
182 if (node instanceof RPMNode) {
183 infoPanel.displayRPM(((RPMNode) node).getRPM());
184 }
185 }
186 });
187
188 return tree;
189 }
190
191 private JMenuBar createMenuBar() {
192 JMenuBar menuBar;
193 JMenu menu;
194 JMenuItem menuItem;
195
196 menuBar = new JMenuBar();
197
198 menu = new JMenu("File");
199 menu.setMnemonic(KeyEvent.VK_F);
200 menuBar.add(menu);
201
202 menuItem = new JMenuItem("Choose dir ...", KeyEvent.VK_C);
203 menuItem.setActionCommand("ChooseDir");
204 menuItem.addActionListener(this);
205 menu.add(menuItem);
206 menu.addSeparator();
207
208 menuItem = new JMenuItem("Exit", KeyEvent.VK_E);
209 menuItem.setActionCommand("Exit");
210 menuItem.addActionListener(this);
211 menu.add(menuItem);
212
213 return menuBar;
214 }
215
216 private JPanel createRPMInfoPane() {
217 return infoPanel;
218 }
219
220 /***
221 * @param file
222 */
223 private void createRPMTree(final File f) {
224 setEnabled(false);
225
226 final Thread thread = new Thread() {
227
228 public void run() {
229 MyMonitor monitor = new MyMonitor(RPMBrowser.this, "Progress",
230 "<html>Searching rpm files in '" + f.getPath()
231 + "'<br>and all subdirectories ...<br></html>",
232 1);
233 List rpms = getRPMFiles(f, monitor);
234 treeModel.setRoot(new GroupNode(f.getPath()));
235 monitor.setVisible(false);
236
237 monitor.setMaximum(rpms.size());
238 monitor.setProgress(0);
239 monitor
240 .setMessage("<html>Reading RPM informations ...<br></html>");
241 monitor.pack();
242 monitor.setVisible(true);
243
244 for (int pos = 0; pos < rpms.size(); pos++) {
245 monitor.incProgress();
246
247 try {
248 RPMFile rpm = new RPMFile((File) rpms.get(pos));
249 rpm.parse();
250 GroupNode node = treeModel.putGroup(rpm.getTag("group")
251 .toString());
252
253 if (node.addRPMNode(new RPMNode(rpm, (File) rpms
254 .get(pos)))) {
255 treeModel.fireTreeStructureChanged(node);
256 }
257 } catch (IOException e) {
258
259 }
260 }
261
262 monitor.setVisible(false);
263 RPMBrowser.this.setEnabled(true);
264 }
265 };
266
267 thread.start();
268 }
269
270 private void quit() {
271 if (quitConfirmed()) {
272 System.exit(0);
273 }
274 }
275
276 private boolean quitConfirmed() {
277 String s1 = "Quit";
278 String s2 = "Cancel";
279 Object[] options = { s1, s2};
280 int n = JOptionPane.showOptionDialog(this,
281 "Do you really want to quit?", "Quit Confirmation",
282 JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null,
283 options, s1);
284
285 if (n == JOptionPane.YES_OPTION) {
286 return true;
287 } else {
288 return false;
289 }
290 }
291
292 class MyRenderer extends DefaultTreeCellRenderer {
293
294 public MyRenderer() {
295 }
296
297 public Component getTreeCellRendererComponent(JTree tree, Object value,
298 boolean sel, boolean expanded, boolean leaf, int row,
299 boolean hasFocus) {
300 super.getTreeCellRendererComponent(tree, value, sel, expanded,
301 leaf, row, hasFocus);
302
303 if (value instanceof RPMNode) {
304 RPMNode node = (RPMNode) value;
305 File[] file = node.getPath();
306 StringBuffer buf = new StringBuffer();
307 buf.append("<html><B>Locations:</B><BR>");
308
309 for (int pos = 0; pos < file.length; pos++) {
310 buf.append(file[pos]);
311
312 if (pos < (file.length - 1)) {
313 buf.append("<BR>");
314 }
315 }
316
317 buf.append("<html>");
318 setToolTipText(buf.toString());
319 } else {
320 setToolTipText(null);
321 }
322
323 return this;
324 }
325 }
326
327 private class GroupNode {
328
329 private Comparator comp = new Comparator() {
330
331 public int compare(Object o1, Object o2) {
332 if ((o1 == null) && (o2 == null)) { return 0; }
333
334 if (o1 == null) { return -1; }
335
336 if (o2 == null) { return 1; }
337
338 return o1.toString().compareTo(o2.toString());
339 }
340 };
341
342 private Set groups = new TreeSet(comp);
343
344 private Set rpms = new TreeSet(comp);
345
346 private String name;
347
348 public GroupNode(String name) {
349 this.name = name;
350 }
351
352 public Object getChild(int count) {
353 Object ret = null;
354
355 if (count < groups.size()) {
356 ret = groups.toArray()[count];
357 } else {
358 ret = rpms.toArray()[count - groups.size()];
359 }
360
361 return ret;
362 }
363
364 public int getChildCount() {
365 return rpms.size() + groups.size();
366 }
367
368 public boolean isLeaf() {
369 return (rpms.size() == 0) && (groups.size() == 0);
370 }
371
372 public boolean addGroupNode(GroupNode node) {
373 if (groups.contains(node)) { return false; }
374
375 groups.add(node);
376
377 return true;
378 }
379
380 public boolean addRPMNode(RPMNode node) {
381 if (rpms.contains(node)) {
382 ArrayList rpmList = new ArrayList(rpms);
383 RPMNode rpmnode = (RPMNode) rpmList.get(rpmList.indexOf(node));
384 File[] paths = node.getPath();
385
386 for (int pos = 0; pos < paths.length; pos++) {
387 rpmnode.addPath(paths[pos]);
388 }
389
390 return false;
391 }
392
393 rpms.add(node);
394
395 return true;
396 }
397
398 public boolean equals(Object obj) {
399 return name.equals(obj.toString());
400 }
401
402 public int hashCode() {
403 return name.hashCode();
404 }
405
406 /***
407 * @param child
408 * @return
409 */
410 public int indexOf(Object child) {
411 int ret = -1;
412
413 if ((ret = (new ArrayList(groups)).indexOf(child)) < 0) {
414 ret = (new ArrayList(rpms)).indexOf(child);
415
416 if (ret >= 0) {
417 ret += groups.size();
418 }
419 }
420
421 return ret;
422 }
423
424 public String toString() {
425 return name;
426 }
427 }
428
429 private class InfoPanel extends JPanel implements ActionListener {
430
431 private DefaultComboBoxModel model = new DefaultComboBoxModel();
432
433 private Hashtable tagLabels = new Hashtable();
434
435 private JComboBox langBox = new JComboBox();
436
437 private RPMFile rpmFile = null;
438
439 public InfoPanel(String[] tags) {
440 langBox.setModel(model);
441
442 GridBagLayout gridLayout = new GridBagLayout();
443 GridBagConstraints c = new GridBagConstraints();
444 c.fill = GridBagConstraints.VERTICAL;
445 c.anchor = GridBagConstraints.NORTHWEST;
446
447 setLayout(gridLayout);
448
449 langBox.setEditable(false);
450 langBox.addActionListener(this);
451 c.gridy = 0;
452
453 JLabel langLabel = new JLabel("Language : ");
454 gridLayout.setConstraints(langLabel, c);
455 add(langLabel);
456 c.gridx = 1;
457 gridLayout.setConstraints(langBox, c);
458 add(langBox);
459 c.gridx = 0;
460 c.gridwidth = 2;
461 c.fill = GridBagConstraints.HORIZONTAL;
462 c.weightx = 1.0;
463
464 for (int pos = 0; pos < tags.length; pos++) {
465 c.gridy = pos + 1;
466
467 TagLabel tagLabel = new TagLabel(tags[pos]);
468 gridLayout.setConstraints(tagLabel, c);
469 add(tagLabel);
470 tagLabels.put(tags[pos], tagLabel);
471 }
472
473 c.weightx = 0;
474 c.weighty = 1.0;
475
476 JPanel fill = new JPanel();
477 gridLayout.setConstraints(fill, c);
478 add(fill);
479 }
480
481
482
483
484 public void actionPerformed(ActionEvent e) {
485 if (rpmFile != null) {
486 String locale = (String) langBox.getSelectedItem();
487
488 if (locale != null) {
489 rpmFile.setLocale(locale);
490 }
491
492 updateView();
493 }
494 }
495
496 public void displayRPM(RPMFile rpm) {
497 rpmFile = rpm;
498
499 langBox.setSelectedIndex(-1);
500 model.removeAllElements();
501
502 String[] langs = rpmFile.getLocales();
503
504 for (int pos = 0; pos < langs.length; pos++) {
505 model.addElement(langs[pos]);
506 }
507
508 updateView();
509 }
510
511 private void updateView() {
512 Enumeration keys = tagLabels.keys();
513
514 while (keys.hasMoreElements()) {
515 String tagName = (String) keys.nextElement();
516 TagLabel label = (TagLabel) tagLabels.get(tagName);
517 DataTypeIf tagValue = rpmFile.getTag(tagName);
518
519 if (tagValue == null) {
520 label.setValue("(none)");
521 } else {
522 label.setValue(tagValue.toString());
523 }
524 }
525 }
526 }
527
528 private class MyMonitor extends JDialog {
529
530 private JLabel label;
531
532 private JProgressBar bar;
533
534 public MyMonitor(JFrame parentComponent, String title, String message,
535 int max) {
536 super(parentComponent);
537 setDefaultCloseOperation(DO_NOTHING_ON_CLOSE);
538 setTitle(title);
539
540 Container content = getContentPane();
541 content.setLayout(new BorderLayout());
542 bar = new JProgressBar(0, max);
543 content.add(bar, BorderLayout.CENTER);
544 label = new JLabel(message);
545 content.add(label, BorderLayout.NORTH);
546 pack();
547 setLocationRelativeTo(parentComponent);
548 setVisible(true);
549 }
550
551 public void setMaximum(int max) {
552 bar.setMaximum(max);
553 }
554
555 public int getMaximum() {
556 return bar.getMaximum();
557 }
558
559 public void setMessage(String message) {
560 label.setText(message);
561 }
562
563 public void setProgress(int p) {
564 bar.setValue(p);
565 }
566
567 public void incProgress() {
568 bar.setValue(bar.getValue() + 1);
569 }
570 }
571
572 private class RPMGroupTreeModel implements TreeModel {
573
574 private GroupNode root = null;
575
576 private Hashtable groups = new Hashtable();
577
578 private Vector treeModelListeners = new Vector();
579
580 public Object getChild(Object parent, int index) {
581 Object ret = null;
582
583 if (parent instanceof GroupNode) {
584 ret = ((GroupNode) parent).getChild(index);
585 }
586
587 return ret;
588 }
589
590 public int getChildCount(Object parent) {
591 int count = 0;
592
593 if (parent instanceof GroupNode) {
594 count = ((GroupNode) parent).getChildCount();
595 }
596
597 return count;
598 }
599
600 public int getIndexOfChild(Object parent, Object child) {
601 int ret = -1;
602
603 if (parent instanceof GroupNode) {
604 ret = ((GroupNode) parent).indexOf(child);
605 }
606
607 return ret;
608 }
609
610 public boolean isLeaf(Object node) {
611 boolean isLeaf = true;
612
613 if (node instanceof GroupNode) {
614 isLeaf = ((GroupNode) node).isLeaf();
615 }
616
617 return isLeaf;
618 }
619
620 public void setRoot(GroupNode root) {
621 GroupNode oldRoot = this.root;
622 this.root = root;
623 groups = new Hashtable();
624
625 if (oldRoot != null) {
626 fireTreeStructureChanged(oldRoot);
627 }
628 }
629
630 public Object getRoot() {
631 return root;
632 }
633
634 public void addTreeModelListener(TreeModelListener l) {
635 treeModelListeners.addElement(l);
636 }
637
638 public void fireTreeStructureChanged(Object obj) {
639 int len = treeModelListeners.size();
640 TreeModelEvent e = new TreeModelEvent(this, new Object[] { obj});
641
642 for (int i = 0; i < len; i++) {
643 ((TreeModelListener) treeModelListeners.elementAt(i))
644 .treeStructureChanged(e);
645 }
646 }
647
648 public GroupNode putGroup(String path) {
649 GroupNode group = null;
650 StringTokenizer pathToken = new StringTokenizer(path, "/");
651
652
653 GroupNode parent = root;
654 String tmp = "";
655 String groupName = null;
656
657 while (pathToken.hasMoreTokens()) {
658 groupName = pathToken.nextToken();
659 tmp += groupName;
660
661 if (!groups.containsKey(tmp)) {
662 group = new GroupNode(groupName);
663 groups.put(tmp, group);
664 } else {
665 group = (GroupNode) groups.get(tmp);
666 }
667
668 if (parent.addGroupNode(group)) {
669 fireTreeStructureChanged(parent);
670 }
671
672 parent = group;
673 tmp += "/";
674 }
675
676 return group;
677 }
678
679 public void removeTreeModelListener(TreeModelListener l) {
680 treeModelListeners.removeElement(l);
681 }
682
683 public void valueForPathChanged(TreePath path, Object newValue) {
684 throw new UnsupportedOperationException();
685 }
686 }
687
688 private class RPMNode {
689
690 private RPMFile rpm;
691
692 private Vector paths = new Vector();
693
694 public RPMNode(RPMFile rpm, File path) {
695 this.rpm = rpm;
696 paths.add(path);
697 }
698
699 public File[] getPath() {
700 return (File[]) paths.toArray(new File[0]);
701 }
702
703 public RPMFile getRPM() {
704 return rpm;
705 }
706
707 public void addPath(File f) {
708 if (!paths.contains(f)) {
709 paths.add(f);
710 }
711 }
712
713 public boolean equals(Object obj) {
714 return toString().equals(obj.toString());
715 }
716
717 public int hashCode() {
718 return toString().hashCode();
719 }
720
721 public String toString() {
722 return rpm.getTag("name").toString() + "-"
723 + rpm.getTag("version").toString() + "-"
724 + rpm.getTag("release").toString();
725 }
726 }
727
728 private class TagLabel extends JPanel {
729
730 private JLabel label = new JLabel();
731
732 private JTextArea value = new JTextArea();
733
734 public TagLabel(String tagName) {
735 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
736 label.setText(tagName);
737 label.setAlignmentX(LEFT_ALIGNMENT);
738 value.setAlignmentX(LEFT_ALIGNMENT);
739 add(label);
740 add(value);
741 setAlignmentX(LEFT_ALIGNMENT);
742 }
743
744 public void setValue(String valueStr) {
745 value.setText(valueStr);
746 }
747 }
748 }